<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>手写Promise-class版-静态方法</title>
  </head>
  <body>
    <script>
      // 手写promise实现步骤：1.构造函数 2.状态及原因 3.then方法 4.异步任务执行 5.链式调用

      const PENDING = "pending";
      const FULFILLED = "fulfilled";
      const REJECTED = "rejected";
      class myPromise {
        status = PENDING;
        result = undefined;

        #handler = []; // [{onFulfilled,onRejected}]
        constructor(fn) {
          const resolve = (result) => {
            if (this.status === PENDING) {
              this.status = FULFILLED;
              this.result = result;
              this.#handler.forEach(({ onFulfilled }) => {
                onFulfilled(this.result);
              });
            }
          };
          const reject = (result) => {
            if (this.status === PENDING) {
              this.status = REJECTED;
              this.result = result;
              this.#handler.forEach(({ onRejected }) => {
                onRejected(this.result);
              });
            }
          };
          try {
            fn(resolve, reject);
          } catch (error) {
            reject(error);
          }
        }
        then(onFulfilled, onRejected) {
          onFulfilled =
            typeof onFulfilled === "function" ? onFulfilled : (value) => value;
          onRejected =
            typeof onRejected === "function"
              ? onRejected
              : (reason) => {
                  throw reason;
                };

          const p2 = new myPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
              runAsyncTask(() => {
                try {
                  const val = onFulfilled(this.result);
                  //   console.log(val)
                  // TypeError: Chaining cycle detected for promise #<Promise>
                  // 处理重复引用
                  resolvePromise(val, p2, resolve, reject);
                  //   console.log("val",val)
                } catch (error) {
                  reject(error);
                }
              });
            } else if (this.status === REJECTED) {
              runAsyncTask(() => {
                try {
                  const val = onRejected(this.result);
                  resolvePromise(val, p2, resolve, reject);
                } catch (error) {
                  reject(error);
                }
              });
            } else if (this.status === PENDING) {
              this.#handler.push({
                onFulfilled: () => {
                  runAsyncTask(() => {
                    try {
                      const val = onFulfilled(this.result);
                      resolvePromise(val, p2, resolve, reject);
                    } catch (error) {
                      reject(error);
                    }
                  });
                },
                onRejected: () => {
                  runAsyncTask(() => {
                    try {
                      const val = onRejected(this.result);
                      resolvePromise(val, p2, resolve, reject);
                    } catch (error) {
                      reject(error);
                    }
                  });
                },
              });
            }
          });

          return p2;
        }

        catch(onRejected) {
          return this.then(undefined, onRejected);
        }

        finally(onFinallly) {
          return this.then(onFinallly, onFinallly);
        }

        static resolve(value) {
          // console.log(value)
          if (value instanceof myPromise) {
            return value;
          }

          return new myPromise((resolve) => {
            resolve(value);
          });
        }

        static reject(reason) {
          return new myPromise((undefined, reject) => {
            reject(reason);
          });
        }

        static race(promiseList) {
          return new myPromise((resolve, reject) => {
            if (!Array.isArray(promiseList)) {
              return reject(new TypeError("Argument is not iterable"));
            }
            promiseList.forEach((p) => {
              // p.then(res=>{
              //   resolve(res)
              // }).catch(err=>{
              //   reject(err)
              // })
              myPromise
                .resolve(p)
                .then((res) => {
                  resolve(res);
                })
                .catch((err) => {
                  reject(err);
                });
            });
          });
        }

        static all(promiseList) {
          return new myPromise((resolve, reject) => {
            if (!Array.isArray(promiseList)) {
              return reject(new TypeError("Argument is not iterable"));
            }
            // 空数组直接兑现
            promiseList.length === 0 && resolve(promiseList);
            const result = [];
            let count = 0;
            promiseList.forEach((p, index) => {
              myPromise.resolve(p).then(
                (res) => {
                  result[index] = res;
                  count++;
                  count === promiseList.length && resolve(result);
                },
                (err) => {
                  reject(err);
                }
              );
            });
          });
        }

        static allSettled(promiseList) {
          return new myPromise((resolve, reject) => {
            if (!Array.isArray(promiseList)) {
              return reject(new TypeError("Argument is not iterable"));
            }
            // 空数组直接兑现
            promiseList.length === 0 && resolve(promiseList);
            const result = [];
            let count = 0;
            promiseList.forEach((p, index) => {
              myPromise.resolve(p).then(
                (res) => {
                  result[index] = { status: FULFILLED, value: res };
                  count++;
                  count === promiseList.length && resolve(result);
                },
                (err) => {
                  result[index] = { status: REJECTED, reason: err };
                  count++;
                  count === promiseList.length && resolve(result);
                }
              );
            });
          });
        }

        static any(promiseList) {
          return new myPromise((resolve, reject) => {
            if (!Array.isArray(promiseList)) {
              return reject(new TypeError("Argument is not iterable"));
            }
            promiseList.length === 0 &&
              reject(
                new AggregateError(promiseList, "All promises were rejected")
              );
            const reasons = [];
            let count = 0;
            promiseList.forEach((p, index) => {
              myPromise.resolve(p).then(
                (res) => {
                  // 只要有一个成功，就直接返回
                  resolve(res);
                },
                (err) => {
                  reasons[index] = err;
                  count++;
                  count === promiseList.length &&
                    reject(
                      new AggregateError(reasons, "All promises were rejected")
                    );
                }
              );
            });
          });
        }
      }

      const p1 = new myPromise((resolve, reject) => {
        setTimeout(() => {
          reject("p1");
        }, 1000);
      });

      const p2 = myPromise.reject("p2");

      const p3 = new myPromise((resolve, reject) => {
        // setTimeout(() => {
        reject("p3");
        // },1000)
      });

      myPromise.any([p1, p2, p3]).then(
        (res) => {
          console.log("any-res", res);
        },
        (err) => {
          console.dir(err);
        }
      );

      /* 实现promise.allSettled()
      const p1 = new myPromise((resolve, reject)=>{
        resolve("1")
      })
      const p2 = myPromise.resolve("2")

      const p3 = new myPromise((resolve, reject)=>{
        setTimeout(() => {
          reject("3")
        }, 1000);
      })

      myPromise.allSettled([p1,p2,p3]).then(res=>{
      // Promise.allSettled([]).then(res=>{
      // Promise.allSettled().then(res=>{
        console.log("allSettled-res",res)
      },err=>{
        console.log("allSettled-err",err)
      }) */

      /* 实现promise.all()
      const p1 = new myPromise((resolve, reject) => {
            resolve("p1");
      })

      const p2 = myPromise.reject("error")

      const p3 = "p3"

      myPromise.all([p1,p2,p3]).then(res=>{
        console.log("promiseAll-res",res)
      }).catch(err=>{
        console.log("promiseAll-err",err)
      }) */

      /* 实现promise.race()
      const p1 = new myPromise((resolve, reject) => {
        setTimeout(() => {
          resolve("p1");
        }, 2000);
      });

      const p2 = new myPromise((resolve, reject) => {
        setTimeout(() => {
          reject("p2");
        }, 1000);
      })

      myPromise.race([p1,p2,"hjhhj"]).then(res=>{
        console.log("resolve",res)
      }).catch(err=>{
        console.log("reject",err)
      }) */

      /* 实现Promise.reject()
       // myPromise.reject("errorrrr").catch(err=>{
      //   console.log("err",err)
      // }) */

      /* 实现Promise.resolve()
    //   myPromise.resolve(new myPromise((resolve,reject)=>{
    //     // resolve("myPromiseResolve1")
    //     reject("myPromiseResolve1Error")
    //   }).then(res=>{
    //     console.log("res",res)
    //   }).catch(err=>{
    //     console.log("error",err)
    //   })
    // )

      // myPromise.resolve("myPromiseResolve2").then(res=>{
      //   console.log("res",res)
      // }) */

      // 用来执行异步的任务
      function runAsyncTask(callback) {
        setTimeout(() => {
          callback();
        }, 0);
      }

      function resolvePromise(val, p2, resolve, reject) {
        if (val === p2) {
          throw new TypeError("Chaining cycle detected for promise #<Promise>");
        }
        if (val instanceof myPromise) {
          val.then(
            (res) => {
              resolve(res);
            },
            (rej) => {
              reject(rej);
            }
          );
        } else {
          resolve(val);
        }
      }

      const nativeThenable = {
        then(resolve) {
          resolve(42);
        },
      };

      Promise.resolve(nativeThenable).then(console.log); // 输出 42
    </script>
  </body>
</html>
